#ifndef PORTABLE_FUNCTION_H_
#define PORTABLE_FUNCTION_H_

#include <iomanip>

#include "src/pbc/portable-bytecode-array.h"
#include "src/pbc/portable-feedback-vector.h"
#include "src/pbc/portable-function-kind.h"
#include "src/pbc/portable-function-syntax-kind.h"

namespace portable {

class Function {
 public:
  Function(uint32_t func_id, String name, FunctionKind kind)
      : function_name_(name),
        scope_(nullptr),
        bytecodes_(nullptr),
        feedback_(nullptr),
        kind_(kind),
        func_id_(func_id),
        flags_(0) {}
  ~Function() {
    if (bytecodes_) delete bytecodes_;
    bytecodes_ = nullptr;
    feedback_ = nullptr;
  }

  FunctionKind kind() const { return kind_; }

  void set_syntax_kind(FunctionSyntaxKind syntax_kind) {
    syntax_kind_ = syntax_kind;
  }
  FunctionSyntaxKind syntax_kind() const { return syntax_kind_; }

  uint32_t func_id() const { return func_id_; }

  String function_name() const { return function_name_; }

  void set_internal_formal_parameter_count(int value) {
    formal_parameter_count_ = value;
  }
  uint16_t internal_formal_parameter_count_with_receiver() {
    return formal_parameter_count_;
  }
  uint16_t internal_formal_parameter_count_without_receiver() {
    return formal_parameter_count_ - 1;
  }

  Scope* scope() { return scope_; }
  void set_scope(Scope* scope) { scope_ = scope; }

  BytecodeArray* bytecode_array() { return bytecodes_; }
  void set_bytecode_array(BytecodeArray* bytecodes) { bytecodes_ = bytecodes; }

  FeedbackVectorSpec* feedback() { return feedback_; }
  void set_feedback(FeedbackVectorSpec* feedback) { feedback_ = feedback; }

  String name() { return function_name_; }

  int function_literal_id() { return literal_id_; }

  void PrintPureName() {
    if (auto str = std::get_if<std::string>(&function_name_)) {
      std::cout << *str;
    } else {
      auto u16str = std::get_if<std::u16string>(&function_name_);
      for (char16_t c : *u16str) {
        std::cout << "\\x" << std::setw(4) << std::setfill('0') << std::hex
                  << static_cast<int32_t>(c);
      }
    }
  }

  void set_language_mode(LanguageMode mode) {
    flags_ = IsStrictBit::update(flags_, mode);
  }

  LanguageMode language_mode() const { return IsStrictBit::decode(flags_); }

  void set_null_name(bool val) { flags_ = IsNullNameBit::update(flags_, val); }

  bool is_null_name() const { return IsNullNameBit::decode(flags_); }

  void set_copyable(bool val) { flags_ = IsCopyableBit::update(flags_, val); }

  bool copyable() const { return IsCopyableBit::decode(flags_); }

  void set_requires_instance_members_initializer(bool val) {
    flags_ = RequiresInstanceMembersInitializerBit::update(flags_, val);
  }

  bool requires_instance_members_initializer() const {
    return RequiresInstanceMembersInitializerBit::decode(flags_);
  }

  void set_length(int value) { length_ = value; }

  uint16_t length() { return length_; }

  void set_suspend_count(uint32_t val) { suspend_count_ = val; }

  uint32_t suspend_count() const { return suspend_count_; }

  uint8_t expected_nof_properties() const { return expected_nof_properties_; }

  void set_expected_nof_properties(uint8_t num) {
    expected_nof_properties_ = num;
  }

 private:
  String function_name_;
  Scope* scope_;
  BytecodeArray* bytecodes_;
  FeedbackVectorSpec* feedback_;
  FunctionKind kind_;
  FunctionSyntaxKind syntax_kind_;
  const uint32_t func_id_;  // index in function list, for serialization
  int flags_;

  uint8_t expected_nof_properties_;

  uint16_t formal_parameter_count_;
  uint16_t length_;
  uint32_t suspend_count_;
  int literal_id_;

  // Encoding of the flags
  using IsStrictBit = v8::base::BitField<LanguageMode, 0, 1>;
  using IsCopyableBit = IsStrictBit::Next<bool, 1>;
  using IsNullNameBit = IsCopyableBit::Next<bool, 1>;
  using RequiresInstanceMembersInitializerBit = IsNullNameBit::Next<bool, 1>;
};

}  // namespace portable

#endif  // PORTABLE_FUNCTION_H_
